﻿using System;
using System.Collections.Generic;
using System.Text;
using ShootSearch.Core;
using Quartz;
using ShootSearch.Util;
using System.Collections;
using System.ComponentModel;
using System.Data;
using log4net;
using System.Threading;
using ShootSearch.Logging;

namespace ShootSearch.Jobs
{
    public abstract class SequenceJob: ISpiderMan
    {
        protected JobExecutionContext context;
        protected string contextPath;
        protected SequenceJobConfig jobConfig;

        #region ISpiderMan Members

        public JobStatus Status { get; protected set; }
        public string StatusSummary { get; protected set; }
        public Task Task { get; set; }
        public ILog Logger { get; set; }

        #endregion

        #region IConfigable Members

        public virtual SpiderManConfig Config
        {
            get { return jobConfig; }
            set
            {
                if (value is SequenceJobConfig)
                {
                    jobConfig = value as SequenceJobConfig;

                }
            }
        }

        #endregion

        #region IInterruptableJob Members

        public virtual void Interrupt()
        {
            if (Status == JobStatus.Running)
            {
                Status = JobStatus.Stopping;
            }
        }
        #endregion

        #region IJob Members

        public void Execute(Quartz.JobExecutionContext context)
        {
            //Load data
            this.context = context;
            Config = context.MergedJobDataMap[Task.JOB_CONFIG] as SequenceJobConfig;
            contextPath = context.MergedJobDataMap[Task.JOB_CONTEXT_PATH] as string;
            contextPath = IO.FormatDirName(contextPath, true);
            contextPath = IO.FormatDirName(contextPath + context.JobDetail.Name, true);
            Task = context.MergedJobDataMap[Task.TAKS_ENTITY] as Task;
            Logger = context.MergedJobDataMap[Task.TAKS_LOGGER] as ILog;

            if (Config == null || string.IsNullOrEmpty(contextPath))
            {
                JobExecutionException e2 = new JobExecutionException(this.GetType() + "Configuration error!");
                // Quartz will automatically unschedule
                // all triggers associated with this job
                // so that it does not run again
                e2.UnscheduleAllTriggers = jobConfig.UnscheduleAllTriggers;
                if (jobConfig.UnscheduleAllTriggers == true && Task!=null)
                {
                    Task.Stop();
                }
                throw e2;
            }
            else
            {
                if (Status == JobStatus.Unstarted)
                {
                    Status = JobStatus.Running;                    
                }
                if (Task != null)
                {
                    Task.NotifiyExcuting(this);
                }
                DoWork(context);
                Log.Info(Logger, "Job excuted!");
                if (Task != null)
                {
                    Task.NotifiyExcuted(this);
                }
                if (Status == JobStatus.Running ||Status == JobStatus.Completing)
                {
                    Status = JobStatus.Completed;                    
                }
                else if (Status == JobStatus.Stopping)
                {
                    Status = JobStatus.Stopped;
                }
            }

            if (jobConfig.LaunchNextJob == LaunchNextJobType.Sequential)
            {
                if (GetNextJobDetail() ==null
                    || (LaunchNextJob(null) && Task != null))
                {
                    Task.NotifyComplete();
                }
            }
            else if (jobConfig.LaunchNextJob == LaunchNextJobType.None && Task != null)
            {
                Task.NotifyComplete();
            }
            
        }

        #endregion

        #region IDisposable Members

        public virtual void Dispose()
        { 
            
        }

        #endregion

        #region LaunchNextJob
        protected JobDetail GetNextJobDetail()
        {
            return context.MergedJobDataMap[Task.JOB_NEXT] as JobDetail;
        }

        protected void SetNextJobParameters(object key, object value)
        {
            JobDetail nextDetail = GetNextJobDetail();
            nextDetail.JobDataMap[key] = value;
        }


        /// <summary>
        /// Launch next job
        /// </summary>
        /// <param name="force">force to launch</param>
        /// <param name="args">The arguments(null is acceptable)</param>
        /// <returns>true launch success, false launch failed</returns>
        public virtual bool LaunchNextJob(Hashtable args)
        {
            JobDetail nextDetail = GetNextJobDetail();            
            if (nextDetail != null)
            {
                nextDetail.JobDataMap[Task.TAKS_LOGGER] = Logger;
                nextDetail.JobDataMap[Task.TAKS_ENTITY] = Task;

                if (args != null)
                {
                    IDictionaryEnumerator ide = args.GetEnumerator();
                    while (ide.MoveNext())
                    {
                        nextDetail.JobDataMap[ide.Key] = ide.Value;
                    }
                }
                context.Scheduler.ScheduleJob(nextDetail, new SimpleTrigger(nextDetail.Name, null));
                Thread.Sleep(1000);
                return true;
            }
            else return false;

        } 
        #endregion        

        public abstract void DoWork(Quartz.JobExecutionContext context);

    }

    [TypeConverter(typeof(Util.ExpandableConverter<SequenceJobConfig>))]
    public class SequenceJobConfig : SpiderManConfig
    {
        /// <summary>
        /// Stop all the jobs when meet an error
        /// </summary>
        [CategoryAttribute("Job"),
                DescriptionAttribute("Unschedule all triggers when meet an error.")]
        public bool UnscheduleAllTriggers { get; set; }
        [CategoryAttribute("Job"),
                DescriptionAttribute("Launch next job after the current job completed.")]
        public LaunchNextJobType LaunchNextJob { get; set; }
        public SequenceJobConfig()
        {
            UnscheduleAllTriggers = true;
            LaunchNextJob = LaunchNextJobType.Sequential;
        }
    }

    public enum LaunchNextJobType
    { 
        None,
        Sequential,
        Manually
    }
}
